home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Collections: Auge 4000
/
Auge 4000 #18 (1988-05-14)(Amiga User Gruppe Einzugsgebiet 4000).zip
/
Auge 4000 #18 (1988-05-14)(Amiga User Gruppe Einzugsgebiet 4000).adf
/
Dfc
/
DFC.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-01-19
|
39KB
|
1,283 lines
/*
* This is dfc, a disk copying and formatting utility written by Radical
* Eye Software. This program is in the public domain; anyone can do
* anything they want with it. This software is made available on an as-is
* basis; don't come to me if you destroy your entire fish disk library
* with it! Of course, it was tested rather extensively before it was
* released . . .
*
* This code was written with Manx 3.4b on February 8, 1988.
*
* dfc is meant to replace both format and diskcopy, and be smaller than
* either. It is meant to work exactly the same as either, depending on
* the command line arguments you give it. In addition, it has several
* additional options, such as buffering of a disk for a quick additional
* copy, the ability to toggle verify mode on and off, and the ability to
* write to multiple disks at the same time. It also has a nice Intuition
* interface. It will not replace the `DiskCopy' or `Format' options from
* the workbench, however; these have to work with the Workbench startup
* message, which this program does not.
*
* This program can be invoked with no command line options. In this case,
* it defaults to a diskcopy from df0: to df1: with verify on and buffering
* on. All keyword options can be preceded by a dash, for you Unix lovers
* out there, and can be abbreviated to one character. The following
* parameters are accepted:
*
* f[rom] disk Use `disk' as the source drive. The format for disk
* is completely free; only the numerals in the argument
* are looked at.
* t[o] disks Use `disks' as the destination drives. Again, the
* format is unspecified.
* v[erify] Verify all writes (default).
* nov[erify] Turn off the verify mode.
* b[uffer] Use extra buffer memory. If your system has enough
* memory, an entire disk will be cached in RAM.
* Otherwise, almost all of the system memory will be
* used.
* nob[uffer] Do not buffer; use minimum memory. (Default.)
* n[ame] diskname Format the destination disk with name `diskname'.
* Otherwise a diskcopy is assumed.
* d[...] A drive argument. Might not have a d; simple numbers
* work as well. The first occurance of such an argument
* sets the source and destination fields; a subsequent
* occurance only sets the destination.
*
* Thus,
* dfc df0: name "Foo bar baz"
* formats drive df0: and names the resultant disk "Foo bar baz".
* dfc df0: df1:
* diskcopies from df0: to df1:.
* dfc -nov 0 123
* diskcopies from drive 0 to drives 1, 2, and 3, with verify turned off.
* dfc from df0: to df0:,df1:,df2:,df3:
* diskcopies from drive df0: to all four drives.
* dfc name Foo
* formats drive df1 with the name "Foo".
*
* Once the program opens its window up, you will have a bunch of gadgets.
* The left-most column of gadgets is the source selection; here you can
* choose either format, or one of the four drives. The next column is
* the destination column; choose any combination of the four drives by
* clicking on the gadgets. Lines will be drawn between these two columns
* indicating the current selections. If a drive is not available, no line
* will be drawn to it.
*
* The topmost gadget is the name gadget. This is used to set the name of
* your disk while formatting.
*
* The `Go' gadget starts the program up; you had better have the disks in
* the drives when you hit this gadget! The `Again' gadget makes another
* copy of the last disk, if buffering is on and enough buffers were
* allocated for an entire disk. This gadget requires all destination
* disks to be in their drives already.
*
* The `Retry' gadget tells the system to retry after a disk error. The
* `Quit' gadget tells the system to abort an operation; you can hit this
* at an error, which aborts the current operation; you can hit this during
* a copy or format operation, which does the same; or you can hit this
* with the drives inactive, which exits the program.
*
* The `Verify' gadget turns the verify mode on and off. The `Buffer'
* gadget turns buffering on and off.
*
* All of these gadgets have keyboard shortcuts which are simply the first
* letter of the gadget name. The source gadgets can be selected with the
* digits `0', `1', `2', or `3'; the destination gadgets with the shifted
* versions of these keys (')', `!', `@', and `#'.) The carriage return
* key is a synonym for `Go', for added diskcopy and format compatibility.
*
* Enjoy this program! Please send any bug reports to Tomas Rokicki,
* Box 2081, Stanford, CA 94309.
*/
#define TITLE "dfc 1 Radical Eye Software"
/*
* A handful of includes for good luck.
*/
#include "intuition/intuition.h"
#include "functions.h"
#include <exec/exec.h>
#include <exec/execbase.h>
#include <devices/trackdisk.h>
#include <libraries/dosextens.h>
/*
* These are the various globals this routine uses.
*/
struct StandardPacket *gpacket ; /* packet to send various things */
struct IntuiMessage *message ; /* the message we are working on */
struct Gadget *gadad ; /* address of gadget from message */
struct Window *window ; /* our window */
struct MsgPort *port ; /* I/O port for dos communication */
struct IntuitionBase *IntuitionBase ;
/* we need Intuition */
struct GfxBase *GfxBase ; /* and graphics */
int havedisk ; /* do we have a full disk in RAM? */
int hibuf ; /* the highest buffer we have */
long output ; /* can we write output? */
char *buffers[81] ; /* pointers to our buffers */
struct IOExtTD *diskreq[4] ; /* our I/O request blocks */
long diskChangeCount[4] ; /* last time disk was changed */
int source = 1 ; /* the source disk, not a mask */
int dest = 2 ; /* all destination drives */
int verify = 1 ; /* verify writes? */
int buffer = 0 ; /* are we using lots of memory? */
char namebuf[32] ; /* this buffer holds disk names */
char df0[] = "DF0:" ; /* use only one copy of this name */
char df1[] = "DF1:" ; /* ditto */
char df2[] = "DF2:" ; /* ditto */
char df3[] = "DF3:" ; /* ditto */
char *df[]={df0, df1, df2, df3}; /* for easy access to drive names */
char blank[] = " " ;
/* used to blank out the middle line */
char msg[] = "Disk df is not complete" ;
/* error message for a disk */
char reading[] = "Reading track xx dfx" ;
/* message for reading disks */
char writing[] = "Writing track xx dfx" ;
/* message for writing disks */
char veriing[] = "Ver'ing track xx dfx" ;
/* message for ver'ing disks */
char nobuf[] = "! couldn't get buffer" ;
/* if we can't get a buffer */
/*
* Always use topaz 80, and let's define an intuition buffer for our
* general string writing stuff.
*/
struct TextAttr myfont = {(STRPTR)"topaz.font", TOPAZ_EIGHTY, 0, 0 };
struct IntuiText intuitext = {1, 0, JAM2, 0, 0, &myfont} ;
/*
* Some defines setting the sizes of various things. Play with these to
* resize things. Too bad string gadgets still have problems with large
* fonts.
*/
#define WIDEGADG 83
#define NARROWGADG 51
#define STRGADG 256
#define GADGHEIGHT 13
#define LINE1 13
#define LINE2 LINE1+11
#define LINE3 LINE2+GADGHEIGHT
#define LINE4 LINE3+GADGHEIGHT
#define LINE5 LINE4+GADGHEIGHT
#define LINE6 LINE5+GADGHEIGHT
#define WINDOWHEIGHT LINE6+GADGHEIGHT+2
#define COL1 4
#define LINESTART COL1+NARROWGADG
#define STRSTART COL1+NARROWGADG+23
#define COL2 COL1+NARROWGADG+42
#define LINEEND COL2-1
#define COL3 COL2+NARROWGADG+10
#define COL4 COL3+WIDEGADG+10
#define WINDOWWIDTH COL4+WIDEGADG+4
#define TRACKSIZE (2*512*11)
/*
* Now, some defines to assist in declaring a few things, and
* centering strings, and the like. These macros make the Intuition
* crap a lot easier to put together.
*/
#define makeintuitext(name,string,size) struct IntuiText name={1,0,JAM2,\
(1+size-8*(sizeof(string)-1))/2,3,&myfont,(UBYTE*)string}
#define makebox(name,tname,width,height,off) short tname[]={0,0,width,0,\
width,1,0,1,0,height-1,width,height-1,width,height,0,height,width,height,\
width,0};\
struct Border name={off,off,1,0,JAM2,10,tname}
#define makengadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
xpos,ypos,NARROWGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
(APTR)&narrowbox,NULL,&text,NULL,NULL,ch}
#define makewgadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
xpos,ypos,WIDEGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
(APTR)&widebox,NULL,&text,NULL,NULL,ch}
/*
* Now we declare all of our structures with the above macros.
* First, the text for our gadgets, and then the gadgets. No sweat.
*/
makeintuitext(formattext,"Format",NARROWGADG);
makeintuitext(df0text,df0,NARROWGADG);
makeintuitext(df1text,df1,NARROWGADG);
makeintuitext(df2text,df2,NARROWGADG);
makeintuitext(df3text,df3,NARROWGADG);
makeintuitext(againtext,"Again",WIDEGADG);
makeintuitext(retrytext,"Retry",WIDEGADG);
makeintuitext(quittext,"Quit",WIDEGADG);
makeintuitext(verifyontext,"Verify On ",WIDEGADG);
makeintuitext(verifyofftext,"Verify Off",WIDEGADG);
makeintuitext(bufferontext,"Buffer On ",WIDEGADG);
makeintuitext(bufferofftext,"Buffer Off",WIDEGADG);
makeintuitext(gotext,"Go",WIDEGADG);
makebox(narrowbox,tn1,NARROWGADG-1,GADGHEIGHT-1,0);
makebox(widebox,tn2,WIDEGADG-1,GADGHEIGHT-1,0);
makebox(strbox,tn3,STRGADG-1,GADGHEIGHT-1,-2);
makengadg(formatgadg,NULL,COL1,LINE2,formattext,'f');
makengadg(sdf0gadg,&formatgadg,COL1,LINE3,df0text,'0');
makengadg(sdf1gadg,&sdf0gadg,COL1,LINE4,df1text,'1');
makengadg(sdf2gadg,&sdf1gadg,COL1,LINE5,df2text,'2');
makengadg(sdf3gadg,&sdf2gadg,COL1,LINE6,df3text,'3');
makengadg(ddf0gadg,&sdf3gadg,COL2,LINE3,df0text,')');
makengadg(ddf1gadg,&ddf0gadg,COL2,LINE4,df1text,'!');
makengadg(ddf2gadg,&ddf1gadg,COL2,LINE5,df2text,'@');
makengadg(ddf3gadg,&ddf2gadg,COL2,LINE6,df3text,'#');
makewgadg(gogadg,&ddf3gadg,COL3,LINE4,gotext,'g');
makewgadg(retrygadg,&gogadg,COL3,LINE5,retrytext,'r');
makewgadg(verifygadg,&retrygadg,COL3,LINE6,verifyontext,'v');
makewgadg(againgadg,&verifygadg,COL4,LINE4,againtext,'a');
makewgadg(quitgadg,&againgadg,COL4,LINE5,quittext,'q');
makewgadg(buffergadg,&quitgadg,COL4,LINE6,bufferontext,'b');
/*
* We need one last gadget, the string gadget, and its associated
* special info structure.
*/
struct StringInfo nameinfo={(UBYTE*)namebuf,NULL,0,31};
struct Gadget namegadg={&buffergadg,STRSTART+2,LINE2+2,STRGADG-4,
GADGHEIGHT-4,GADGHCOMP,STRINGCENTER|RELVERIFY,STRGADGET,(APTR)&strbox,
NULL,NULL,NULL,(APTR)&nameinfo,'n'};
/*
* Now we have our window structure. Initially not resizeable.
*/
struct NewWindow newwindow = {200,20,WINDOWWIDTH,WINDOWHEIGHT,0,1,
CLOSEWINDOW|VANILLAKEY|GADGETDOWN|GADGETUP,
WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|SMART_REFRESH|ACTIVATE,
&namegadg,NULL,(UBYTE *)TITLE,NULL,NULL,-1,-1,-1,-1,WBENCHSCREEN};
/*
* Now we start coding. This routine draws a string into the window at
* a specific X, Y location.
*/
draw(s, x, y)
char *s ;
int x, y ;
{
intuitext.IText = (UBYTE *)s ;
PrintIText(window->RPort, &intuitext, (long)x, (long)y) ;
}
/*
* This routine gives us those pretty little DF0:BUSY things, which keep
* AmigaDOS from futzing with the drives when we are playing with them.
*/
inhibit(d, t)
int d ;
long t ;
{
struct MsgPort *handler ;
struct StandardPacket *packet = gpacket ;
handler = (struct MsgPort *)DeviceProc(df[d]) ;
if (handler == NULL || port == NULL)
return ;
packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt) ;
packet->sp_Pkt.dp_Link = &(packet->sp_Msg) ;
packet->sp_Pkt.dp_Port = port ;
packet->sp_Pkt.dp_Type = ACTION_INHIBIT ;
packet->sp_Pkt.dp_Arg1 = t ;
PutMsg(handler, packet) ;
WaitPort(port) ;
GetMsg(port) ;
}
/*
* This routine looks at the global message and returns a character
* indicating the selected gadget. This gives us easy equivalence
* of gadgets to vanillakeys, for instance. This also replymsg()'s
* the message.
*/
int getop() {
register long class ;
register int op ;
short code ;
class = message->Class ;
code = message->Code ;
op = ' ' ;
gadad = (struct Gadget *)(message->IAddress) ;
ReplyMsg(message) ;
message = NULL ;
if (class == CLOSEWINDOW) {
op = 'q' ;
} else if (class == GADGETDOWN || class == GADGETUP) {
op = gadad->GadgetID ;
} else if (class == VANILLAKEY) {
op = code ;
gadad = NULL ;
}
return(upcase(op)) ;
}
/*
* We don't want to queue up messages, because the user might hit
* 'g' 100 times accidentally. We flush all pending messages, and
* return the last operation. This way, this routine can be used
* to see if the user typed 'Q' to exit some operation.
*/
int disposemsgs() {
register int op = 0 ;
while (message = (struct IntuiMessage *)
GetMsg(window->UserPort))
op = getop() ;
return(op) ;
}
/*
* This is put into a function to help make the program smaller.
* It takes a character and makes sure it is uppercase.
*/
int upcase(c)
register int c ;
{
if ('a' <= c && c <= 'z')
return(c-32) ;
else
return(c) ;
}
/*
* Here we wait for a quit or retry key. We also accept go, space,
* and carriage return.
*/
int abortretry() {
register int op ;
disposemsgs() ;
while (1) {
while ((message = (struct IntuiMessage *)
GetMsg(window->UserPort))==NULL)
WaitPort(window->UserPort) ;
op = getop() ;
if (op == 'Q' || op == 'R' || op == ' ' || op == 'G' || op == 10)
return(op) ;
}
}
/*
* This routine pads a string out to 40 characters. Used to write to
* the top line of the window.
*/
static char ibuf[41] ;
char *to40(s)
register char *s ;
{
register int i = 0 ;
register char *p = ibuf ;
while (*s != 0)
p[i++] = *s++ ;
while (i < 40)
p[i++] = ' ' ;
return(p) ;
}
/*
* This error routine draws a message up at the top of the screen, and
* waits for a response. After it gets one, it returns the response.
* It clears the top line afterwards.
*/
int error(s)
register char *s ;
{
int op ;
DisplayBeep(NULL) ;
if (*s == '!' || !window) {
if (output) {
Write(output, s, (long)strlen(s)) ;
Write(output, "\n", 1L) ;
}
cleanup() ;
} else {
draw(to40(s), COL1, LINE1+2) ;
op = abortretry() ;
}
draw(to40(""), COL1, LINE1+2) ;
return(op) ;
}
/*
* If the user selects a set of drives, this routine first tries to
* allocate them. Then, based on the success of the allocation, it
* draws lines in the window indicating the source and destination
* drives.
*/
redrawlines() {
register int i, j ;
allocdisks() ;
SetAPen(window->RPort, 0L) ;
RectFill(window->RPort, (long)LINESTART, (long)LINE2, (long)STRSTART-1,
(long) LINE6+GADGHEIGHT) ;
RectFill(window->RPort, (long)STRSTART, (long)LINE3, (long)LINEEND,
(long)LINE6+GADGHEIGHT) ;
SetAPen(window->RPort, 1L) ;
i = LINE3 + (GADGHEIGHT+1)/2 + source * GADGHEIGHT ;
for (j=0; j<4; j++)
if (dest & (1 << j)) {
Move(window->RPort, (long)LINESTART, (long)i) ;
Draw(window->RPort, (long)LINEEND,
(long)(LINE3 + (GADGHEIGHT+1)/2 + j * GADGHEIGHT)) ;
}
}
/*
* This routine parses a string, usually something like 'df0:' or
* 'df0:,df1:,df2:', but can be even '012', into just a mask indicating
* which drives were selected. It simply looks for the characters '0',
* '1', '2', and '3'.
*/
int getmask(s)
register char *s ;
{
register int t = 0 ;
while (*s != 0) {
if ('0' <= *s && *s <= '3')
t |= 1 << (*s - '0') ;
s++ ;
}
return(t) ;
}
/*
* This is our exit routine. It frees the drives, deletes the ports,
* buffers, closes the window, and libraries. Then it exits.
*/
cleanup() {
register int i ;
for (i=0; i<4; i++)
FreeDisk(i) ;
if (gpacket)
FreeMem(gpacket, (long)sizeof(struct StandardPacket)) ;
if (port)
DeletePort(port) ;
freebuffers() ;
if (window)
CloseWindow(window) ;
if (GfxBase)
CloseLibrary(GfxBase) ;
if (IntuitionBase)
CloseLibrary(IntuitionBase) ;
exit(0) ;
}
/*
* This routine attempts to allocate m buffers. We try and leave at
* least 32K of memory, even with the allocations. We set hibuf at the
* exit point.
*/
getbuffers(m)
int m ;
{
register int i ;
if (buffers[80]==NULL &&
(buffers[80]=(char *)
AllocMem((long)TRACKSIZE, MEMF_CHIP | MEMF_PUBLIC))==NULL)
error(nobuf) ;
hibuf = 0 ;
for (i=0; i<m; i++)
if (buffers[i]==NULL &&
(AvailMem(MEMF_PUBLIC) < 32000 ||
(buffers[i]=(char *)AllocMem((long)TRACKSIZE, MEMF_PUBLIC))==NULL))
break ;
else
hibuf = i+1 ;
if (hibuf < 1)
error(nobuf) ;
for (; i<80; i++)
if (buffers[i] != NULL) {
FreeMem(buffers[i], (long)TRACKSIZE) ;
buffers[i] = NULL ;
}
}
/*
* And this routine lets them all go.
*/
int freebuffers() {
register int i ;
for (i=0; i<81; i++)
if (buffers[i] != NULL) {
FreeMem(buffers[i], (long)TRACKSIZE) ;
buffers[i] = NULL ;
}
}
/*
* We attempt to create an I/O request. We allocate memory for it, and
* then initialize some of the ports.
*/
struct IORequest *CreatExtIO() {
register struct IORequest *ioReq ;
ioReq = (struct IORequest *)AllocMem((long)sizeof(struct IOExtTD),
MEMF_CLEAR | MEMF_PUBLIC) ;
if (ioReq == NULL)
return (NULL) ;
ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE ;
ioReq->io_Message.mn_Node.ln_Pri = 0 ;
ioReq->io_Message.mn_ReplyPort = port ;
return(ioReq) ;
}
/*
* This routine frees an I/O request.
*/
DeleteIO(ioExt)
struct IORequest *ioExt ;
{
FreeMem(ioExt, (long)sizeof(struct IOExtTD)) ;
}
/*
* Now we try to allocate a disk. First, we attempt to allocate an
* I/O request, and then we actually open the device. If either of
* these fail, we return 0. This can occur if a drive is opened that
* doesn't exist. After we have the disk, we inhibit AmigaDOS from
* putzing with it.
*/
int OpenDisk(i)
register int i ;
{
register struct IOExtTD **p = diskreq+i ;
if (*p)
return(1) ;
if ((*p = (struct IOExtTD *)CreatExtIO()) &&
OpenDevice(TD_NAME, (long)i, *p, 0L) == 0) {
inhibit(i, TRUE) ;
return(1) ;
} else {
if (*p) {
DeleteIO(*p) ;
*p = NULL ;
}
return(0) ;
}
}
/*
* Here we release a disk. We check that we have it first! Then, we
* close the device, uninhibit the drive, kill the I/O request, and
* exit.
*/
FreeDisk(i)
register int i ;
{
register struct IOExtTD **p = diskreq+i ;
if (*p) {
CloseDevice(*p) ;
inhibit(i, FALSE) ;
DeleteIO(*p) ;
*p = NULL ;
}
}
/*
* This routine attempts to allocate all of the disks we need, based on the
* current settings of source and dest. If a disk cannot be allocated, it
* is removed from both source or dest. Source might be set to point to df0,
* if this occurs. Then, if we can't allocate df0, we exit fatally. This
* occurance can actually happen, if some other program has df0:. (I think.)
*/
allocdisks() {
register int i, need ;
top:
need = dest ;
if (source != -1)
need |= 1 << source ;
for (i=0; i<4; i++, need >>= 1) {
if (need & 1) {
if (! OpenDisk(i)) {
dest &= ~(1 << i) ;
if (source == i) {
if (i == 0)
error("! I couldn't allocate the internal drive") ;
source = 0 ;
goto top ;
}
}
} else
FreeDisk(i) ;
}
}
/*
* We call this routine if we are going to be accessing this
* disk. It gets the changecount, so our read/write routines work.
*/
int InitDisk(d)
register int d ;
{
register struct IOExtTD *p = diskreq[d] ;
int result ;
p->iotd_Req.io_Command = TD_CHANGENUM ;
DoIO(p) ;
result = (diskChangeCount[d] != p->iotd_Req.io_Actual) ;
diskChangeCount[d] = p->iotd_Req.io_Actual ;
return(result) ;
}
/*
* The plural of the above routine takes a mask and initializes all of
* the drives. It adds the source drive to the list automatically.
*/
initdisks(mask)
int mask ;
{
int d ;
if (source != -1)
mask |= (1 << source) ;
for (d=0; d<4; d++)
if (mask & (1 << d))
InitDisk(d) ;
}
/*
* Kill the motor of a drive. So someone can stick disks in and out.
*/
motoroff(d)
register int d ;
{
register struct IOExtTD *p = diskreq[d] ;
p->iotd_Req.io_Length = 0 ;
p->iotd_Req.io_Command = TD_MOTOR ;
DoIO(p) ;
}
/*
* This routine is a fast machine-language block move, that moves
* exactly one block of data. Do not change TRACKSIZE and expect
* this still to work!
*/
fcpy(dest, src)
long *dest, *src ;
{
#asm
movem.l a0-a6/d0-d7,-(a7)
move.l 12(a5),a0
move.l 8(a5),a1
move.l #43,d0
lsdf:
movem.l (a0)+,a2-a6/d1-d7
movem.l a2-a6/d1-d7,(a1)
add.w #48,a1
movem.l (a0)+,a2-a6/d1-d7
movem.l a2-a6/d1-d7,(a1)
add.w #48,a1
movem.l (a0)+,a2-a6/d1-d7
movem.l a2-a6/d1-d7,(a1)
add.w #48,a1
movem.l (a0)+,a2-a6/d1-d7
movem.l a2-a6/d1-d7,(a1)
add.w #48,a1
movem.l (a0)+,a2-a6/d1-d7
movem.l a2-a6/d1-d7,(a1)
add.w #48,a1
movem.l (a0)+,a2-a5
movem.l a2-a5,(a1)
add.w #16,a1
dbra d0,lsdf
movem.l (a7)+,a0-a6/d0-d7
#endasm
}
/*
* Another fast assembly language routine for verifying a buffer. This
* routine returns 0 if the two buffers are the same, and something else
* otherwise.
*/
int fcmp(dest, src)
long *dest, *src ;
{
register int foo = 0 ;
#asm
movem.l d0/a0/a1,-(a7)
move.l 12(a5),a0
move.l 8(a5),a1
move.l #2815,d0
alsdf:
cmp.l (a0)+,(a1)+
dbne d0,alsdf
move.w d0,d4
addq.w #1,d4
movem.l (a7)+,a0/a1/d0
#endasm
return(foo) ;
}
/*
* This routine turns on a particular gadget.
*/
turnon(g)
register struct Gadget *g ;
{
if (g->Flags & GADGDISABLED) {
SetAPen(window->RPort, 0L) ;
RectFill(window->RPort, (long)g->LeftEdge, (long)g->TopEdge,
(long)g->LeftEdge+g->Width-1,
(long)g->TopEdge+g->Height-1) ;
SetAPen(window->RPort, 1L) ;
OnGadget(g, window, NULL) ;
}
}
/*
* This routine turns off a particular gadget.
*/
turnoff(g)
register struct Gadget *g ;
{
if (!(g->Flags & GADGDISABLED))
OffGadget(g, window, NULL) ;
}
/*
* Our main copy routine. The variable j holds the current destinations
* that are being written into; as disks drop like flies, j will drop
* them; at the end, we print a message about all the disks that dropped
* out. We start by initializing the disks.
*/
goforit(again)
int again ;
{
register int i, j, k, t ;
register int ohbuf ;
turnoff(&gogadg) ;
turnoff(&verifygadg) ;
turnoff(&buffergadg) ;
turnoff(&againgadg) ;
turnon(&retrygadg) ;
turnoff(&namegadg) ;
RefreshGadgets(window->FirstGadget, window, NULL) ;
j = dest ;
initdisks(j) ;
ohbuf = hibuf ;
/*
* If we are formatting, we only use one buffer. This avoids the
* unsightly delay which happens if we build up the formatted disk in
* memory first; the user wonders what the hell is going on. We first
* check that the user isn't trying to read and write from the same disk;
* if he is, he will have to swap about 160 times, so we tell him no go.
*/
if (source == -1)
ohbuf = 1 ;
if (ohbuf == 1 && source >= 0 && (dest & (1 << source))) {
while (error("No buffering?") == 'R') ;
goto finishup ;
}
for (t=0; t<80; t+=ohbuf) {
if (! again) {
for (k=0; k<ohbuf && t+k<80; k++) {
if (disposemsgs()=='Q')
goto aborted ;
if (getdata(source, t+k, k)==0)
goto finishup ;
}
if (source != -1) {
if (dest & (1 << source)) {
for (i=0; i<4; i++)
if (j & (1 << i))
motoroff(i) ;
do {
if (error("Enter destination disks")=='Q')
goto finishup ;
} while (! InitDisk(source)) ;
initdisks(j) ;
}
}
if (ohbuf == 80)
havedisk = 1 ;
}
for (k=0; k<ohbuf && t+k<80; k++) {
if (disposemsgs()=='Q')
goto aborted ;
for (i=0; i<4; i++) {
if (j & (1 << i)) {
if (writedata(i, t+k, k)==0) {
j &= ~(1 << i) ;
motoroff(i) ;
}
}
}
}
if (! again) {
if (source != -1 && (dest & (1 << source)) && t+k < 80) {
motoroff(source) ;
do {
if (error("Enter source disk")=='Q')
goto finishup ;
} while (! InitDisk(source)) ;
}
}
}
goto finishup ;
/*
* On exit, we turn off all the motors, clear out the middle line, and
* return.
*/
aborted:
j = 0 ;
finishup:
for (i=0; i<4; i++)
if (diskreq[i])
motoroff(i) ;
for (i=0; i<4; i++)
if ((dest-j) & (1<<i)) {
msg[7] = i + '0' ;
error(msg) ;
}
draw(blank, COL3, LINE3+3) ;
disposemsgs() ;
}
/*
* This routine writes a given message to the screen; either reading,
* writing, or ver'ing. It fills in the track and disk number. It
* checks first that the source isn't `format', which is created behind
* the scenes instead of being read from an actual disk. If we are
* reading from track 40, we get the name of the disk and put it up on
* the screen.
*/
writeop(s, t, d)
register char *s ;
register int t, d ;
{
if (d != -1) {
s[14] = '0' + t / 10 ;
s[15] = '0' + t % 10 ;
s[19] = '0' + d ;
draw(s, COL3, LINE3+3) ;
}
}
/*
* This routine gets data from disk d, track t, into buffer b.
* If the disk is -1, it gets it from the format routine (later.)
* We return 1 if success; 0 if failure. If there is an error, we allow
* the user to retry as many times as he likes.
*/
int getdata(d,t,b)
register int d, t, b ;
{
register struct IOExtTD *p = diskreq[d] ;
register int i = 0 ;
if (d==-1) {
makeformatdata(t, b) ;
return(1) ;
}
do {
writeop(reading, t, d) ;
p->iotd_Req.io_Length = TRACKSIZE ;
p->iotd_Req.io_Data = (APTR)buffers[80] ;
p->iotd_Req.io_Command = ETD_READ ;
p->iotd_Count = diskChangeCount[d] ;
p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
DoIO(p) ;
fcpy(buffers[b], buffers[80]) ;
} while (p->iotd_Req.io_Error != 0 &&
((i=error("Read Error; Quit/Retry?"))!='Q')) ;
if (t==40 && i != 'Q')
writename(b) ;
return (i != 'Q') ;
}
/*
* writedata is analagous to the above routine. However, if verify is
* turned on, then we read the data back in from the disk and make sure
* that it is correct. Note that whenever we write track 40, we first
* update the root block creation date and last modified date.
*/
int writedata(d,t,b)
register int d, t, b ;
{
register struct IOExtTD *p = diskreq[d] ;
register int i = 0 ;
top:
if (t==40)
updaterootblock(b) ;
do {
writeop(writing, t, d) ;
fcpy(buffers[80], buffers[b]) ;
p->iotd_Req.io_Length = TRACKSIZE ;
p->iotd_Req.io_Data = (APTR)buffers[80] ;
p->iotd_Req.io_Command = TD_FORMAT ;
p->iotd_Count = diskChangeCount[d] ;
p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
DoIO(p) ;
} while (p->iotd_Req.io_Error != 0 &&
((i=error("Write Error; Quit/Retry?"))!='Q')) ;
if (i=='Q')
return(0) ;
if (verify) {
writeop(veriing, t, d) ;
p->iotd_Req.io_Length = TRACKSIZE ;
p->iotd_Req.io_Data = (APTR)buffers[80] ;
p->iotd_Req.io_Command = ETD_READ ;
p->iotd_Count = diskChangeCount[d] ;
p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
DoIO(p) ;
if ((p->iotd_Req.io_Error != 0 ||
fcmp(buffers[80], buffers[b])) &&
((i=error("Verify Error; Quit/Retry?"))!='Q')) {
if (t==0)
InitDisk(d) ;
goto top ;
}
}
return (i != 'Q') ;
}
/*
* This routine creates data for the format option. Note the clever
* way the data is built up; this routine should build a disk exactly
* the same way the standard AmigaDOS format does. If we are on track
* 40, some additional work must be done to create a root block and
* bitmap, but it's not too bad.
*/
makeformatdata(t, b)
int t, b ;
{
register long *p ;
register long cs ;
register long i ;
unsigned char *q ;
p = (long *)buffers[b] ;
cs = 'DOS\0' + (((long)t & 48) << 16) ;
for (i=0; i<TRACKSIZE/4; i++)
*p++ = cs + (i & 3327) ;
if (t != 40)
return ;
p = (long *)buffers[b] ;
for (i=0; i<256; i++)
*p++ = 0 ;
p = (long *)buffers[b] ;
p[0] = 2 ;
p[3] = 0x48 ;
p[78] = 1 ;
p[79] = 0x371 ;
q = (unsigned char *)(p + 108) ;
*q++ = strlen(namebuf) ;
strcpy(q, namebuf) ;
p[127] = 1 ;
p += 128 ;
for (i=1; i<55; i++)
p[i] = 0xffffffff ;
p[0] = 0xc000c037 ;
p[28] = 0xffff3fff ;
p[55] = 0x3fffffff ;
}
/*
* This routine recalculates the checksum for a block, and updates it in
* word 5. The sum of all the words in a block must be 0.
*/
recheck(w)
register long *w ;
{
register int i ;
register long cs ;
cs = 0 ;
for (i=0; i<128; i++)
cs += w[i] ;
w[5] -= cs ;
}
/*
* We simply DateStamp the creation date, modification date, and
* rechecksum the block.
*/
updaterootblock(b)
register int b ;
{
DateStamp(buffers[b] + 420) ;
DateStamp(buffers[b] + 484) ;
recheck(buffers[b]) ;
}
/*
* If we read from track 40, we write the name on the screen in
* the string gadget supplied for that purpose.
*/
writename(b)
int b ;
{
int i = buffers[b][432] ;
int j ;
RemoveGadget(window, &namegadg) ;
if (i > 31)
i = 31 ;
for (j=0; j<i; j++)
namebuf[j] = buffers[b][j + 433] ;
namebuf[j] = 0 ;
AddGadget(window, &namegadg, -1L) ;
RefreshGadgets(&namegadg, window, NULL) ;
}
/*
* And finally, our main routine! This thing is awfully long; they do
* get that way sometimes, don't they?
*/
main(argc, argv)
int argc ;
char *argv[] ;
{
int op ;
register char *p, *q ;
int seen = 0 ;
output = (long)Output() ;
/*
* First, we parse the arguments. Arguments allowed are documented at
* the top of this file. If there is a dash as the first character of
* an argument, we ignore it, thus allowing Unix-style options.
*/
while (argc > 1) {
argc-- ;
argv++ ;
p = *argv ;
if (argc > 1)
q = argv[1] ;
else
q = "" ;
if (*p == '-')
p++ ;
switch (upcase(*p)) {
/*
* If the argument starts with a D, or a number, it specifies a drive.
* Actually, it might be a `DRIVE' keyword, which would return a mask of
* 0, so we check the return value for 0.
*/
case '0' : case '1' : case '2' : case '3' : case 'D' :
dest = getmask(p) ;
if (dest != 0) {
if (! seen)
source = dest ;
seen = 1 ;
}
break ;
/*
* The from keyword sets the source . . .
*/
case 'F' :
argc-- ;
argv++ ;
source = getmask(q) ;
seen = 1 ;
break ;
/*
* The to keyword sets the destination . . .
*/
case 'T' :
argc-- ;
argv++ ;
dest = getmask(q) ;
break ;
/*
* This could either be a `nobuffer', `noverify', or `name' keyword.
* We check for any of these.
*/
case 'N' :
if (p[1]=='o' || p[1]=='O') {
if (p[2]=='v' || p[2]=='V')
verify = 0 ;
else if (p[2]=='b' || p[2]=='B')
buffer = 0 ;
else goto errorarg ;
} else {
if (strlen(q) > 30)
q[30] = 0 ;
strcpy(namebuf, q) ;
source = 0 ;
argc-- ;
argv++ ;
}
break ;
/*
* Verify keyword is easy
*/
case 'V' :
verify = 1 ;
break ;
/*
* As is the buffer keyword.
*/
case 'B' :
buffer = 1 ;
break ;
/*
* We go ahead and print an error message if we didn't understand an
* option.
*/
default:
errorarg:
if (output) {
Write(output, "Unknown option ", 15L) ;
Write(output, p, (long)strlen(p)) ;
Write(output, "\n", 1L) ;
}
}
}
/*
* Up to this point, the source has been a mask. Now we turn it into an
* integer.
*/
if (source & 8)
source = 3 ;
else if (source & 4)
source = 2 ;
else if (source & 2)
source = 1 ;
else if (source & 1)
source = 0 ;
else
source = -1 ;
/*
* We initialize a few gadgets based on the parameters the user selected.
*/
if (verify) {
verifygadg.GadgetText = &verifyontext ;
} else {
verifygadg.GadgetText = &verifyofftext ;
}
if (buffer) {
buffergadg.GadgetText = &bufferontext ;
hibuf = 80 ;
} else {
buffergadg.GadgetText = &bufferofftext ;
hibuf = 1 ;
}
/*
* And now we try and open things up! First intuition, then graphics,
* then our window and an I/O port. If any of these fail, we simply
* exit.
*/
if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
"intuition.library",33L))!=NULL &&
(GfxBase = (struct GfxBase *)OpenLibrary(
"graphics.library",0L))!=NULL &&
(window=OpenWindow(&newwindow))!=NULL &&
(port=CreatePort(0L, 0L)) &&
(gpacket=(struct StandardPacket *)AllocMem(
(long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))) {
/*
* We draw the lines between what the user has selected, and enter into
* our main command loop. Then, we wait for a message. After getting one,
* we drop into a case statement keying off what message it was.
*/
redrawlines() ;
getbuffers(hibuf) ;
while (1) {
if (havedisk)
turnon(&againgadg) ;
else
turnoff(&againgadg) ;
turnoff(&retrygadg) ;
turnon(&gogadg) ;
turnon(&verifygadg) ;
turnon(&buffergadg) ;
if (source == -1)
turnon(&namegadg) ;
else
turnoff(&namegadg) ;
RefreshGadgets(window->FirstGadget, window, NULL) ;
while ((message = (struct IntuiMessage *)
GetMsg(window->UserPort))==NULL)
WaitPort(window->UserPort) ;
op = getop() ;
switch(op) {
/*
* A shift-0, shift-1, shift-2, or shift-3 toggles the appropriate bit
* in the destination, and redraws the lines. A 0, 1, 2, or 3 sets the
* source to that drive, and continues.
*/
case ')' :
dest ^= 1 ;
goto redrawem ;
case '!' :
dest ^= 2 ;
goto redrawem ;
case '@' :
dest ^= 4 ;
goto redrawem ;
case '#' :
dest ^= 8 ;
goto redrawem ;
case '0' : case '1' : case '2' : case '3' :
source = op - '0' ;
redrawem :
redrawlines() ;
break ;
/*
* If verify is selected, we toggle the state of the verify flag, and
* update the gadget to reflect this state.
*/
case 'V' :
verify = ! verify ;
RemoveGadget(window, &verifygadg) ;
if (verify) {
verifygadg.GadgetText = &verifyontext ;
} else {
verifygadg.GadgetText = &verifyofftext ;
}
AddGadget(window, &verifygadg, -1L) ;
RefreshGadgets(&verifygadg, window, NULL) ;
break ;
/*
* The buffer option does essentially the same thing, only for buffering.
*/
case 'B' :
buffer = ! buffer ;
RemoveGadget(window, &buffergadg) ;
if (buffer) {
buffergadg.GadgetText = &bufferontext ;
getbuffers(80) ;
} else {
buffergadg.GadgetText = &bufferofftext ;
getbuffers(1) ;
}
havedisk = 0 ;
AddGadget(window, &buffergadg, -1L) ;
RefreshGadgets(&buffergadg, window, NULL) ;
break ;
/*
* If the user selects `format', then we set the source appropriately and
* redraw the lines. Of course, we no longer have a disk, as when the
* format is executed, it will destroy buffer 0.
*/
case 'F' :
source = -1 ;
havedisk = 0 ;
goto redrawem ;
/*
* The name gadget, if invoked from the keyboard with the `n' key,
* Activates the string gadget.
*/
case 'N' :
if (gadad == NULL && source == -1) {
ActivateGadget(&namegadg, window, NULL) ;
}
break ;
/*
* The `G' gadget, or carriage return, starts us on our merry way.
*/
case 'G' : case 10 : case 32 : case 13 :
goforit(0) ;
break ;
/*
* The `A' gadget insures that we have a disk first, otherwise it
* complains.
*/
case 'A' :
if (! havedisk) {
while (error("No disk in memory")=='R') ;
} else {
goforit(1) ;
}
break ;
/*
* Retry is ignored in the main command loop, as there is nothing to
* retry!
*/
case 'R' :
/*
* Quit is handled at the bottom of the loop.
*/
case 'Q' :
/*
* As a default, we do nothing; not even an error message.
*/
default :
break ;
}
/*
* If the last gadget selected was 'Q', we exit.
*/
if (op == 'Q')
break ;
}
} else {
error("! couldn't open window") ;
}
/*
* Release memory and exit.
*/
cleanup() ;
}